Net Talk
edited by Roberto Bagnara

Editor's Note: this issue's NetTalk has been selected from discussions taking place in the very active SWI-Prolog Mailing list.

Content:



Good Examples of Properly Commented Prolog Code
From: Roberto Bagnara


Can anyone point me to (very) good examples of properly commented Prolog code?  I am looking for coding and commenting styles and practices upon which to base the development of rather big applications.




From: Steve Moyle
Subject: Re: Good Examples of Properly Commented Prolog Code

One place to start would be Covington's guidlines:

[PDF] Some Coding Guidelines for Prolog
http://www.ai.uga.edu/mc/plcoding.pdf

I am interested how this thread develops, particularly those that have experience of professional development in Prolog.




From: Paulo Moura
Subject: Re: Good Examples of Properly Commented Prolog Code

Roberto Bagnara wrote:
> can anyone point me to (very) good examples of properly commented
> Prolog code?  I am looking for coding and commenting styles and
> practices upon which to base the development of rather big
> applications.

Maybe the following page will give you some ideas:
http://www.logtalk.org/contributions/iso8601.html



From: Richard A. O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code

Steve Moyle wrote:
> One place to start would be Covington's guidlines:
>
> [PDF] Some Coding Guidelines for Prolog
> http://www.ai.uga.edu/mc/plcoding.pdf

I have a few quibbles with that.

1.1 Begin every predicate (except auxiliary predicates) with an introductory comment in the standard format.
Quibble:  There is no "THE" standard format. The form I use begins with

%   <name>(<mode><var>: <type>, ..., <mode><var>: <type>)
%   is true when .....

Covington is half right when he says that the modes + - ? are "not  part of the Prolog language"; the thing is that they WERE part of the DEC-10 Prolog language.
It can be useful to extend the set of modes:
so for example it is simply wrong to write
compare(?R: order, +T1: term, +T2: term)
because T1 and T2 are allowed to be variables, and
compare(?R: order, ?T1: term, ?T2: term)
is misleading because we expect that ? arguments will be unified with something, but
compare(?R: order, =T1: term, =T2: term)
is just right.

In DEC-10 Prolog, - really truly genuinely did mean "MUST be uninstantiated", and code that got that wrong could go seriously insane at run time.  Using "-" to mean "normally uninstantiated" is seriously misleading and I wish Covington hadn't recommended that. The difference between > and ? is that a ? argument may be allowed to control what the predicate does, but a > argument should not.

Covington advises that "Comments of this type are not needed for auxiliary predicates" on the grounds that "users of your code need not know about" them.
I agree that anything users of your code need to know about should be commented.  I do NOT agree that auxiliary predicates do not need to be commented.  The convention we used at Quintus was that any auxiliary predicate in a library file which needed commenting got a comment exactly like any other predicate, except that the first line started with "%.  " instead of "%   ".  We had a tool (which I wrote) that extracted comments for a searchable catalogue, and it knew to ignore "%." comments.

Auxiliary predicates in Prolog often correspond to loops in other languages; the meaning of the Prolog predicate is the loop invariant, and really should be stated in a comment.

1.2 Use descriptive argument names in the introductory comment; they need not be the same as those in the clause.
Quibble:  they need not, but there should be an extremely obvious  family relationship.  (Possibly a qualifying prefix or a numeric suffix.)  Normally, the names *should* be the same whenever that is practical.

2.1 Make all names pronounceable.
Absolutely!

2.2 Never use two different names that are likely to be pronounced alike.
Also true, although there is much more variation in the pronunciation of English than some Americans appear to realise, so it can be quite hard to tell how things will be pronounced.  (I would never have believed that some people would pronounce 'char' as 'care' if I hadn't heard it for myself.  I do NOT regard it as reasonable to go out of my way to avoid using both 'char' and 'care'.)

2.3 Construct predicate names with lower-case letters, separating words with underscores.
Absolutely!  The single most evil thing about Java was including  "_" in the language and then telling people to use baStudlyCaps.

2.4 Do not mix up to, two, and too.
Saving characters was necessary in languages with short identifiers (Fortran up to and including Fortran 77, limit 6; C89 with limit 8 for external identifiers) or file systems with short names (V7 UNIX, 14 characters; MS-DOS, 8+3).  Prolog is not such a language.

2.5 Within names, do not express numbers as words.
Quibble: you *can't* make spellings 100% predictable from pronunciations.  The route from spellings to pronunciations is about 99% predictable in English, but there are relics of two different scribal traditions and spelling is not 100% predictable from pronunciation.  For example, "blue" and "blew" sound the same.
I recommend that exported predicates should not have numeric suffixes unless the number is some sort of code number.  For  example,
unicode_4_0_0(?Code, ?Class)
might be reasonable.

2.6 Identify auxiliary predicates by appending _aux or _x, _xx, and so forth.
There are no occurrences of that convention in the Quintus library and I am glad of it.  _x and _xx are particularly bad; even numbers  are better than that.  If an auxiliary predicate is there to do a case analysis, '_case' is a better suffix than '_aux'.  If an auxiliary predicate is a loop, '_loop' is a better suffix than  '_aux'.  There is practically *always* something better to use than '_aux'.

I am as lazy as any other programmer.  But I did eventually notice  that I could *type* a short predicate name and get the computer to expand it to something longer, either by using vi or emacs abbreviations or by simply going back and search/replacing.

The thing that is particularly bad about _x _xx _xxx is that they all look pretty much alike; at least _1 _2 _3 do not resemble each other that much.

2.7 If a predicate represents a property or relation, its name should be a noun, noun phrase, adjective[, adjectival phrase], prepositional phrase (????), or indicative verb phrase.
See Ledgard & Tauer, "Professional Software", for more detailed advice on naming.  Do _not_ call everything in sight "is_xxx".

2.8 If a predicate is understood procedurally - that is, its job is to do something rather than to verify a property - its name should be an imperative verb phrase.
Ledgard & Tauer again.  The second evil thing about Java is its massive overuse of "get".  Do not use "get" for fetching a property.

2.9 Place arguments in the following order: inputs, intermediate results, and final results.
This is a rather oversimplified version of the advice in "The Craft of Prolog".

2.10 Consider how your arguments map onto ordinary English.
Or whatever language you prefer to think in, of course.

3.1 Use descriptive names for variables wherver possible, and make them  accurate.
Good naming practice in any language.

3.2 Construct variable names with mixed-case letters, using capitalisation to set off words.
This was the convention that I followed for many years.  The longer I live, the less I like it.  Covington (and the younger O'Keefe) were right to prefer ResultSoFar to Result_so_far, but I tend to think these days that Result_So_Far is even better.

3.3 For variables of purely local significance, use single letters.
ARGH!  Since all variables in Prolog are local to a single clause, surely _all_ variables are "of purely local significance"?  I once had the misfortune to have to maintain some code written in this style and I never want to have to do that again.

The question is "how hard is it to read the code".  If there is anything tricky about it, give your reader some help.
If you have a file-wide convention that "N is always the total number of elements to process, I is the number of elements processed so far"  then you can freely use N and I.  If you have a convention that L and U are always the lower and upper bounds of some range, fine. If you have a convention that C0, C1, C2, C are always character codes, S0, S1, S2, S are sequences, and so on, fine.  But that convention must *not* be "purely local".

3.4 Use a single letter for the first element of a list and a plural name for the remaining elements.
This is a bent, even broken, version of the usual rule, which is "use a singular name for list elements and the corresponding plural name for the whole list".  I regard Covington's [T|Tree] example as *bad* because it is inconsistent.  If one thing is a T, why aren't several of them Ts?  If several of them are Trees, why isn't one of them a Tree?  [Tree|Trees] is best, [T|Ts] is OK (if there is something in the context that helps you figure out that T means Tree), but [T|Tree] is not good at all.

4.1 Use /* */ only to comment out blocks of code; use % for explanatory comments.
I strongly disagree.  For one thing, you really should not be 'commenting out', and if you are, /* */ comments don't actually work (as C programmers, if they are any good, are well aware).

In a typical Prolog source file, you will have some sort of standard header with meta-data about the file (name, author, revision, copyright, &c).  Then you'll have Prolog :- module declaration and maybe other declarations.

Any then you should have a big comment explaining what the file is all about and what the central ideas are, with maybe a picture of a data structure.

This code should be readable as plain text with a minimum of distracting punctuation.  That means that it has to be a /* */ comment, with *no* extra stars.  (But possibly dashes at the top and bottom.)
If a file divides naturally into several pieces, each piece can have its own such comment.

4.2 Use layout to make comments more readable.
Use layout to make *anything* more readable.

4.3 If code needs elaborate explanation, consider rewriting it.
Sound advice for any language, but sometimes the result of  consideration will be "nope, it's already as simple as we could hope for, it's just a hard problem".

4.4 Indent all but the first line of each clause.
Yes, and begin each clause on a new line.

4.5 If a test is unnecessary because a cut has guaranteed that it is true, say so in a comment at the appropriate place.
I'm not sure that this is always necessary; I am sure that it never hurts.

4.6 Indent an additional 2 spaces between 'repeat' and the corresponding cut.
I agree about indenting, but why 2?  I use 4. Also, sometimes the logically final cut is not physically final and shouldn't be.

4.7 Put each subgoal on a separate line, except closely related pairs such as write and nl.
Generally good advice.  If you are willing to use format/2, you don't even need to put write and nl on the same line.  It's a great  pity the ISO Prolog substandard didn't include anything like format/2.

4.8 (A) Skip a line between clauses. (B) Skip two lines between predicates.

4.9 Keep clauses less than 25 lines if possible.
So they fit on a 24x80 screen, of course. I've never paid much attention to this in languages like Fortran and C.  Since in Prolog you *can't* have a loop without splitting out another predicate, and case analyses *often* (but not always) deserve their own predicates too, it is no hardship to follow this rule in Prolog.

4.10 Consider redesigning any non-auxiliary predicate that has more than 4 arguments.
I count roughly 70 such exported predicates in the Quintus library.  For example,

    %   anti_unify(+Term1, +Term2, -Subst1, -Subst2, -Term)
    %   binds Term to a most specific generalisation of Term1 and Term2,
    %   and Subst1 and Subst2 to substitutions such that
    %       Subst1(Term) = Term1
    %       Subst2(Term) = Term2
    %   Substitutions are represented as lists of Var=Term pairs, where
    %   Var is a Prolog variable, and Term is the term to substitute for Var.
    %   When you call it, Subst1, Subst2, and Term should be variables.

This has 2 inputs and 3 outputs; it's hard to see how it could have  fewer than 5 arguments, and it only does one thing.

    %   ask_number(+Prompt, +Lower, +Upper, +Default, >Answer)
    %   asks a question where the answer should be a number in the range
    %   Lower..Upper.  If the user enters an empty line, the Default
    %   (which need not be in range, or even a number) is returned.
    %    A typical use might be ask_number('Percentage', 0, 100, 50, Percentage).

This has four inputs and one output.  I suppose the four arguments could be packed into a data structure, but that data structure would have no other uses.  In fact, it would likely make uses of this predicate *more* complicated, not less, to try to reduce the arity.
I will agree that benchmark:time/15 has far too many arguments,  but I didn't write that one.

    %   substring(+ABC: text(), ?B: text(),
    %          ?A_Len: integer, ?B_Len: integer, ?C_Len: integer)
    %   is true when ABC and B are both strings or both atoms and
    %   there exist texts A and C such that ABC = A ++ B ++ C,
    %   length(A) = Len_A, length(B) = Len_B, length(C) = Len_C.
    %   The whole string argument (ABC) must be supplied;
    %   this predicate can solve for all the others.

Again, this predicate *needs* 5 arguments.
Also bear in mind the possibility of using term-expansion to supply extra arguments in a systematic way.  A non-terminal  with 4 arguments is every bit as reasonable as a predicate with 4 arguments, even though a non-terminal with 4 arguments  "is" a predicate with 6 arguments (or in some translations, 7 or even 8).

4.11 Consider using a pretty-printer for finished printouts.
The only time a program is "finished" is when its scrapped. If you want fancy printing, use a literal programming tool.  I've used fancy listers (this was standard in Interlisp-D), and I came to hate things that changed the font size.  What if I wanted to concentrate on the comments rather than the procedure headings?  Why would I want the comments in italics or in teeny tiny script or changed so they don't look like  comments?
(Having said that, I have a fontiser/colouriser of my own. On very rare occasions it is useful.  But for ordinary listsings, it's a pain.  It is especially a pain because what you see in the listing DOES NOT MATCH what you see in Emacs.

5.1 Invest appropriate (not excessive) effort in the program; distinguish a prototype ffrom a finished product.
Good advice for any language.

5.2 The most efficient program is the (sic.) one that does the right computation, not the one with the most tricks.
Of course there may be more than one program that does a right computation.  The old (Kernighan?) advice "make it simple to make it fast" still holds.

5.3 When efficiency is critical, make tests.
Absolutely!

5.4 Conduct experiments by writing separate small programs, not by mangling the main one.
Nope.  Small programs tell you about the performance of small programs, not the real program.  The only satisfactory way to  tell if a change will help the real program is to change the real program and measure it.  Covington seems to be advising  people who do not have or do not use any version control system.
Make sure the working sources are checked into version control because changing them for "efficiency".  Consider making a new version control branch for efficiency experiments.

5.5 Use cuts sparingly but precisely.
Nice advice, but how?  See The Craft of Prolog.

5.6 Never add a cut to correct an unknown problem.
Absolutely!

5.7 Avoid the semicolon (;) - make separate clauses instead.
I disagree.  His example is very artificial.  I originally followed this advice, until it dawned on my that most of the time there *wasn't* any natural concept and it was extremely hard to give the case analysis predicate any sensible name.
The important thing here is not avoiding semicolons but giving  things their own identities and names whenever there is a helpful name that they *can* be given.

5.8 Use parentheses whenever operator precedence is important: do not assume that people have memorised the precedence table.
Hmm.  Why don't we just abandon operator syntax entirely then and just use Lispy syntax?  (Actually, I *like* Lispy syntax.)
In the specific case of x, y ; z I agree that parentheses are advisable, but I'd write it as
    (   x,
        y
        ;   z
        )
where the layout provides the important clue about precedence  I find code using otiose parentheses like (x, y) ; z disgusting and difficult to read.
The important thing here is

5.9 When you use ; always use parentheses
But around the *whole* form, *not* around the conjunctions.

5.10 Avoid if-then-else structures.u
I strongly disagree.  Very strongly indeed.  If-then-elses are MUCH better than cuts.  This is especially true in Mercury, of course.

5.11 Look out for constructs that are almost always wrong.
True.

5.12 Work at the beginning of the list
True, but don't be fanatical about it.

5.13 Avoid append as far as possible.
Use append liberally in *design*; use difference pairs or grammar rules in *implementation*.

5.14 Use difference lists to achieve fast concatenation.
Quibble:  I regard the term "differnce lists" as misleading. I use "difference pairs" (of arguments) or "list differences". Apart from that, fine.
There are other ways to get fast concatenation, of course.

5.15 Use tail recursion for recursions of unknown depth.
I don't see what "unknown depth" has to do with it.  I would say "prefer tail recursion to body recursion, but don't be scared of body recursion if you need it".

5.16 Recognise that tail recursion is unimportant when the depth of the recursion is limited to about 50 or less.
I agree that you should not "sacrifice simplicity and clarity" to achieve microscopic gains, but I have never found it useful to ask "is this recursion limited to about 50 or less".  Even for short recursions (and what counts as "short" on a 1GB machine is different from what used to count as "short" on 1MB machiens) tail recursion is often the most natural way to express things.
I'd say that the rule is "use tail recursion whenever it is a clear way to say what you want, WHATEVER the likely depth of recursion; use body recursion if that's the easiest way to get the code right, WHATEVER the likely depth of recursion".  Worry about restructuring *after* you have measured what the code is actually doing.

5.17 Avoid assert and retract unless you actually need to preserve information through backtracking.
I would also say "If you have a dynamic predicate, write interface  predicates for changing it instead of using 'bare' calls to assert and retract, so that your interface predicates can check  that the data base will still be logically consistent after the change."

5.18 For sorting, use merge sort or a built-in sorting algorithm.
He has the details wrong:  Quicksort is *NOT* O(n log n), it is O(n^2) and this worst case _often_ happens in practice.  (Yes, I know about the arguments in textbooks that say it's extremely unlikely for uniform random inputs; the problem is that real-world inputs are not uniform random inputs.)

5.19 Instead of sorted lists, consider using trees.
"A binary tree never needs sorting because it is never out of order"; however, it DOES need work, sometimes rather complicated work, to KEEP it in order.  In a WAM-based implementation where a list requires  2 words per element and other compound terms with N arguments require N+1 words, a tree will cost twice as much storage as a list.
The *right* rule is "use binary trees if you need element-wise access; use sorted lists if you can do most things on whole collections."

6.1 Isolate non-portable code.
Good advice for any language.

6.2 Isolate "magic numbers".
In fact the example of pi is a very good one.  I once had to use a Prolog system from one of our competitors where one part of their library used a 64-bit value for pi and another part used an 80-bit value for pi.  Not surprisingly, trig code got rather messed up.

6.3 take the extra minute to prevent errors rather than having to find them later.
Good advice for any language.

6.4 Test code at its boundaries (limits).
Good advice for any language.

6.5 Test that each loop starts correctly, advances correctly, and ends correctly.
Good advice for any language; could go further.

6.6 Test every predicate by forcing it to backtrack.
Misleadingly worded:  many predicates *can't* backtrack. "Test every predicate by failing back into it"; now that's something you *can* do.

6.7 Test predicates by supplying arguments of the wrong types.
I'm not so sure about this one.  For a beginner, it may be useful advice so that they find out what is likely to provoke which error message.  But append/3, for example, is designed to work with lists; it isn't *intended* to do anything in particular if given arguments of any other type, so there is no sense in which you can test it for other types.  (To test something, there must be a specified set of acceptable behaviours so that you can tell whether the actual behaviour is acceptable or not.  If "anything goes", then the "test" cannot fail.)

6.8 Do not waste time testing for errors that will be caught anyhow.
That'd be nice, except that we do not in general *know* which errors "will be caught anyhow".  6.8 appears to contradict 6.7.

6.9 In any error situation, make the program either correct the problem or crash (not just fial).
True.  "Crashing" here means calling raise_exception/1 (which the ISO Prolog substandard very confusingly renamed to throw/1).

6.10 Make error messages informative.
Good advice, but error reports should be related to the exported predicate they originated from, *not* to some internal predicate which the user doesn't know about.
When we added exception handling to Quintus Prolog, some built-in predicates were given error handlers just so that they could handle internal deeply generated exceptions and map them back to user-level descriptions.

6.11 Master the Prolog debugger; it is simple, powerful, and portable.
Simple, yes.  Powerful, yes, although it's even better with the advice package.  Portable, not as much as it could be.  There are 3-port, 4-port, 5-port, and even 7-port debuggers out there, and that's without considering exceptions.  I find it difficult to use the SWI debugger because the keystrokes that years of using Quintus Prolog programmed into my fingers don't work in SWI.  (One of the reasons I'd rather --disable-readline.)

6.12 Use write for debugging.
Wrong.  Use print for debugging; it was specifically intended for that.

6.13 Mark all temporarily altered lines of code with "%TEST!!!".
I don't find this very useful; if I want to know what I changed,  I just ask SCCS.

6.14 Use write(!!!) to mark places in the program where work remains to be done.
I've never found that particularly useful either.
Something slightly more useful is
    shouldnt :-
        write(user, 'This should not happen'), nl(user),
        break,
        throw(shouldnt).
(adapted from Interlisp).



From: Michael Maxwell
Subject: Re: Good Examples of Properly Commented Prolog Code

Richard O'Keefe wrote:
> 6.13 Mark all temporarily altered lines of code with "%TEST!!!".
>
>     I don't find this very useful; if I want to know what I changed,
>     I just ask SCCS.
>
> 6.14 Use write(!!!) to mark places in the program where work remains
>     to be done.
>
>     I've never found that particularly useful either.

Sometimes these comments persist through multiple revisions of a file, so SCCS or RCS aren't useful ways to point them out.
FWIW, my favorite editor (Visual SlickEdit) allows color coding of constructs on a programming language-specific basis.  Most editors these days do (including jEdit).

What dawned on me awhile back is that my editor allows me to define user-specific tokens, with their own color coding, and I can use that to put tokens like '%TEST!!!' or '%!!!' in some color that stands out, like red.  That way temporarily altered lines, or things that I want to fix but don't have the time to do right now, are easily spotted when I scroll through a buffer.  (If I used TreeWare, and a color printer, this would work too.)

This color coding of course doesn't work for someone else who is reading my file, unless we standardize the user-defined tokens.  But Michael Covington's suggested use of '!!!' at least provides some visual cue.



From: Jan Wielemaker
Subject:  Re: Good Examples of Properly Commented Prolog Code

Richard A. O'Keefe wrote:
> Steve Moyle wrote:
>> One place to start would be Covington's guidlines:
>>
>> [PDF] Some Coding Guidelines for Prolog
>> http://www.ai.uga.edu/mc/plcoding.pdf
>
> I have a few quibbles with that.

Not sure my understanding of `a few' is the same as yours :-)

>     The form I use begins with
>
>     %   <name>(<mode><var>: <type>, ..., <mode><var>: <type>)
>     %   is true when .....

I like this very much, except I'd like to have a blank line between the first semi-formal line and the description, starting with % Sofar I didn't use types, but I might start doing that. For a long time I plan to add some code to the help-system that makes this documentation available in a transparent way. Still didn't find the time to do it :-( Here are is a typical example of what I use

%    age(?Name, ?Age)
%
%    True if person named Name has age Age.

> 2.6 Identify auxiliary predicates by appending _aux or _x, _xx,
>     and so forth.
>
>     There are no occurrences of that convention in the Quintus library
>     and I am glad of it.  _x and _xx are particularly bad; even numbers
>     are better than that.  If an auxiliary predicate is there to do
>     a case analysis, '_case' is a better suffix than '_aux'.  If an
>     auxiliary predicate is a loop, '_loop' is a better suffix than
>     '_aux'.  There is practically *always& something better to use
>     than '_aux'.
>
>     I am as lazy as any other programmer.  But I did eventually notice
>     that I could *type* a short predicate name and get the computer to
>     expand it to something longer, either by using vi or emacs
>     abbreviations or by simply going back and search/replacing.
>
>     The thing that is particularly bad about _x _xx _xxx is that they
>     all look pretty much alike; at least _1 _2 _3 do not resemble each
>     other that much.

Not so sure. I still struggle with the naming of auxiliary predicates. _loop is nice if it applies, but quite often there is no sensible name to give to them. _x* works fine to about 3-4 levels, which is often enough. To me _xx and _2 are about equal.

>     This was the convention that I followed for many years.  The longer
>     I live, the less I like it.  Covington (and the younger O'Keefe)
>     were right to prefer ResultSoFar to Result_so_far, but I tend to think
>     these days that Result_So_Far is even better.

I prefer ResultSoFar.

> 4.6 Indent an additional 2 spaces between 'repeat' and the corresponding
> cut.
>
>     I agree about indenting, but why 2?  I use 4.
>     Also, sometimes the logically final cut is not physically final
>     and shouldn't be.

I used to do that, but since the days of auto-indenting editors things get more tricky.  Because where I like this for repeat, it should apply to any generator in a failure-driven loop.  I used to write

    (   member(X, List),
           process(X),
        fail
    ;   true
    )

But even PceEmacs cannot determine when this layout is appropriate and I hate doing the indentation by hand.  So I stopped using this.  Today I use something like this:

    (   repeat,
        action,
        condition
    ->  true
    ).

Where applicable, use forall(generator(X), action(X)).  It also avoids failure of action/1 go unnoticed.  Better a program saying 'No' than producing the wrong result.

> 4.7 Put each subgoal on a separate line, except closely related pairs
>     such as write and nl.
>
>     Generally good advice.  If you are willing to use format/2, you

True, but quite a few interesting systems have it and the implementations are reasonable compatible.

> 4.8 (A) Skip a line between clauses.
>     (B) Skip two lines between predicates.
>
>     (A) I flatly disagree.  Amongst other things, my editor lets me move
>         to the beginning of the previous procedure with one (Meta-Ctrl-R)
>         command (or the next, with Ctrl-_ Meta-Ctrl-R).  Sticking in
>         those silly blank lines stuffs this up completely, and it is far
>         too nice a feature to let someone stuff it up.

I agree with Richard.  Next clause on next line, next related predicate one blank line and next unrelated predicate two blank lines works just fine.  The not indented clause head is enough to seperate the clauses.

> 4.9 Keep clauses less than 25 lines if possible.
>
>     So they fit on a 24x80 screen, of course.
>
>     I've never paid much attention to this in languages like Fortran and
>     C.  Since in Prolog you *can't* have a loop without splitting out
>     another predicate, and case analyses *often* (but not always)
>     deserve their own predicates too, it is no hardship to follow this
>     rule in Prolog.

Unless you have I/O like (including graphics calls) predicates ... I've seen _very_ long clauses, even overflowing the 512 subclause limit the system had very long ago (now unlimited).

> 4.11 Consider using a pretty-printer for finished printouts.
>
>     The only time a program is "finished" is when its scrapped.
>     If you want fancy printing, use a literal programming tool.
>     I've used fancy listers (this was standard in Interlisp-D),
>     and I came to hate things that changed the font size.  What
>     if I wanted to concentrate on the comments rather than the
>     procedure headings?  Why would I want the comments in italics
>     or in teeny tiny script or changed so they don't look like
>     comments?
>
>     (Having said that, I have a fontiser/colouriser of my own.
>     On very rare occasions it is useful.  But for ordinary listsings,
>     it's a pain.  It is especially a pain because what you see in
>     the listing DOES NOT MATCH what you see in Emacs.

Might be a bit outdated. Who prints programs these days with nice large colour displays? I've not printed code for years. Do get yourself a 1280x1024 or bigger monitor. Much better investment than a faster CPU.

> 5.3 When efficiency is critical, make tests.
>
>     Absolutely!

Use the profiler.  Use the graphical debugger to examine choice-points and see whether they are as you expect them to be.  Be aware that tests are good, but performance depends on the Prolog environment.  I try to ensure performance of a particular construct does not deteriorate much in later versions, but sometimes an alternative construct may become much faster.

> 5.5 Use cuts sparingly but precisely.
>
>     Nice advice, but how?  See The Craft of Prolog.

True.

> 5.10 Avoid if-then-else structures.
>
>     I strongly disagree.  Very strongly indeed.  If-then-elses are
>     MUCH better than cuts.  This is especially true in Mercury, of course.

I tend to use a predicate if I can give it a meaningful name, i.e. if by giving it a name the readability of the main clause improves. Otherwise use if-then-else.

> 5.17 Avoid assert and retract unless you actually need to preserve
>     information through backtracking.
>
>     I would also say "If you have a dynamic predicate, write interface
>     predicates for changing it instead of using 'bare' calls to
>     assert and retract, so that your interface predicates can check
>     that the data base will still be logically consistent after the
>     change."

These seems more complimentary than conflicting statements. Both are true. Using assert/retract as a substitute for global variables is a really bad idea. It often harms performance, it make the nice `retry' option of the debugger worthless, it has trouble with cleanup, re-entrance and threads.  If you really need them, write an interface as Richard suggests.  Especially true in multi-threaded applications as you probably need explicit synchronisation using mutexes.

> 6.4 Test code at its boundaries (limits).
>
>     Good advice for any language.

Time for a good documented testing skeleton ...  There are various out there of different quality.

> 6.11 Master the Prolog debugger; it is simple, powerful, and portable.
>
>     Simple, yes.  Powerful, yes, although it's even better with the
>     advice package.  Portable, not as much as it could be.  There are
>     3-port, 4-port, 5-port, and even 7-port debuggers out there, and
>     that's without considering exceptions.  I find it difficult to use
>     the SWI debugger because the keystrokes that years of using Quintus
>     Prolog programmed into my fingers don't work in SWI.  (One of the
>     reasons I'd rather --disable-readline.)

It took me very long, mostly because after I write it hardware was too slow, but these days I rarely touch the normal debugger anymore. 99% of the cases I use the graphical one.  Unfortunately sometimes it goes nuts :-(

> 6.12 Use write for debugging.
>
>     Wrong.  Use print for debugging; it was specifically intended for that.

use debug(Channal, Format, Arguments). You can leave them in your code with no overhead if you compile using -O and they act as comments too.

> 6.13 Mark all temporarily altered lines of code with "%TEST!!!".
>
>     I don't find this very useful; if I want to know what I changed,
>     I just ask SCCS.

True.  I strongly dislike files with commented dead code.  Only, use CVS or subversion :-)

> 6.14 Use write(!!!) to mark places in the program where work remains
>     to be done.
>
>     I've never found that particularly useful either.
>
>     Something slightly more useful is
>
>     shouldnt :-
>         write(user, 'This should not happen'), nl(user),
>         break,
>         throw(shouldnt).
>
>     (adapted from Interlisp).

Most of the time I put % TBD: whatever at the end of the line. Sometimes I simply call tbd('Whatever'). That will cause the system to raise an undefined predicate. Even better, list_undefined/0 will report them and where they are called.



From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code


Jan Wielemaker wrote:
> I like this very much, except I'd like to have a blank line between the
> first semi-formal line and the description, starting with %

When the comment starts out like this:
     %   <name>(<mode><var>: <type>, ..., <mode><var>: <type>)
     %   is true when .....
the first line is the subject of the first sentence. It is really very jarring
to put a blank line in the *middle* of a sentence.

> %    age(?Name, ?Age)
> %
> %    True if person named Name has age Age.

I would write that as

    %   age(?Name: atom, ?Age: number)
    %   is true when Name is the name of a person
    %   and the age of that person in years is Age.

or without the types as

    %   age(?Name, ?Age)
    %   is true when Name is the name of a person (as an atom)
    %   and the age of tha person in years is Age (as a number).

In this case, I might leave out the (as a number) bit; what else would it be?  But the encoding of the name is _not_ obvious and needs saying.  A general convention within a file that names are atoms (as opposed to character lists or strings or even name(Family,Personal) or whever) would mean you don't have to say it every time.

>> 2.6 Identify auxiliary predicates by appending _aux or _x, _xx,
>>     and so forth.
>
> Not so sure.  I still struggle with the naming of auxiliary
> predicates.

Why, so do I.

> _loop is nice if it applies, but quite often there is no
> sensible name to give to them.  _x* works fine to about 3-4
> levels, which is often enough.  To me _xx and _2 are about
> equal.

I disagree.  I don't find that _x works to *any* number of levels, and I have real trouble with the idea of 4 levels of "miscellaneous" auxiliary predicates in the first place.  Given that one of the first Prolog programs I worked on was a geometry theorem prover (I didn't write the thing, I just revised it), I still think of _x as referring geometric co-ordinate and expect the next two in the series to be _y and _z.

Perhaps we could advance this discussion further if Jan provided an example where he wanted to use _x _xx and _xxx and see what I can do about naming them.

>>     This was the convention that I followed for many years.  The longer
>>     I live, the less I like it.  Covington (and the younger O'Keefe)
>>     were right to prefer ResultSoFar to Result_so_far, but I tend to think
>>     these days that Result_So_Far is even better.
>
> I prefer ResultSoFar.

As I said, I _used_ to.  These days, I am less tolerant of things that violate the normal spacing rules for text.

>> 4.6 Indent an additional 2 spaces between 'repeat' and the corresponding
>> cut.
>>
>>     I agree about indenting, but why 2?  I use 4.
>>     Also, sometimes the logically final cut is not physically final
>>     and shouldn't be.
>
> I used to do that, but since the days of auto-indenting editors

When I first heard about Emacs, I was ever so keen. When I actually got my hands on Emacs, it was a real disappointment. Emacs' auto-indentation for C was *horrible*, and it turned out to be impossible to reach a tolerable style by fiddling with its parameters. To this day, one of the first things I do with an autoindenting editor is switch automatic indentation *OFF*.

The editor I use is Emacs-*like*.  For indentation,

    Return    -> make a new line with no indentation below the
           current line, move the cursor to the new line
    ^_ Return    -> like Return, but new line is above old line,
    Line feed    -> make a new line with the same indentation as the
           current line, below the current line, and move
           the cursor to it (just after the indentation)
    ^_ Line feed-> like Line feed, but new line is above old line.
    Meta ^I    -> indent current line by one step (default 4,
           settable by ^U n Meta +)
    ^U n $^I    -> indent by n steps
    Meta ^U    -> outdent current line by one step
    ^U n $^U    -> outdent by n steps
    Meta i    -> indent current region (mark to point) one step
    ^U n $i    -> indent current region n steps
    Meta u    -> outdent current region (mark to point) one step
    ^U n $u    -> outdent current region n steps

> things get more tricky.

Indeed things get tricky.  That's because all the autoindenters I've come across are far more trouble than they are worth.

> Because where I like this for repeat, it should apply
> to any generator in a failure-driven loop.  I used to write
>
>     (   member(X, List),
>            process(X),
>         fail
>     ;   true
>     )

OK, how would we do that in my editor?
    "(   member(X, List)," ^J            21
    $^I $^I "process(X)," ^J            16
    $^U "fail" ^J                     7
    $^U ";   true" ^J                11
    ")"                         1
                    Total:        56 keystrokes.

Who needs an auto-indenter? Hmm.  I've just added a very simple auto-indent feature. If enabled,
    "("  at the start of a line adds indent-1 spaces after it;
    ";"  at the start of a line indents, adds ";", and indent-1 spaces;
    "->" at the start of a line indents, adds "->", and indent-2 spaces;
    ")"  at the start of a line outdents unless the previous line
     began with "(" ";" or "->".
    LF   indents the new line one step if the previous line starts
         with "(" ";" or "->".

So we get

    "(member(X, List)," ^J                18
    $^I "process(X)," ^J                14
    $^U "fail" ^J                     7
    ";true" ^J                     6
    ")"                         1
                    Total:        46 keystrokes.

Autoindenting doesn't save all that many keystrokes.

But I actually disagree.  I do NOT think "it should apply to any generator in a failure-driven loop".  There is something very very special about repeat/0 which does NOT apply to member/2 (at least in normal use), namely that repeat/0 never fails.  It has and is intended to have infinitely many solutions.  My layout for failure-driven loops is

    (   member(X, List)
        process(X),
        fail ; true
    ),

just try and teach _that_ to an auto-indenter.  Note the signature of a failure-driven loop:  "fail ; true".  A repeat loop does NOT have that signature.  A repeat loop has to be terminated by some kind of cut.

> Where applicable, use forall(generator(X), action(X)).

Agreed.

> Better a program saying 'No' than producing the wrong result.

Absolutely!

>> 4.9 Keep clauses less than 25 lines if possible.
>>
>>     So they fit on a 24x80 screen, of course.
>>
>>     I've never paid much attention to this in languages like Fortran and
>>     C.  Since in Prolog you *can't* have a loop without splitting out
>>     another predicate, and case analyses *often* (but not always)
>>     deserve their own predicates too, it is no hardship to follow this
>>     rule in Prolog.
>
> Unless you have I/O like (including graphics calls) predicates
> ... I've seen _very_ long clauses, even overflowing the 512
> subclause limit the system had very long ago (now unlimited).

At Quintus we had a bug report from a customer:  they had a clause with more than 64000 variables!  It was machine-generated, of course.

>> 4.11 Consider using a pretty-printer for finished printouts.
>
> Might be a bit outdated.  Who prints programs these days with
> nice large colour displays?

Me.

> Do get yourself a 1280x1024 or bigger monitor.

I *have* a 1280x1024 monitor.  In fact both my SunBlade100 and my G4 Mac have such monitors.  You *still* don't get a whole lot of text on them, and if you want to really carefully read a file, paper is *wonderful*. I tend to find that syntax colouring just makes stuff less readable (because it reduces the contrast, which is well known to reduce readability).

>> 5.10 Avoid if-then-else structures.
>>
>>     I strongly disagree.  Very strongly indeed.  If-then-elses are
>>     MUCH better than cuts.  This is especially true in Mercury, of course.
>
> I tend to use a predicate if I can give it a meaningful name, i.e. if by
> giving it a name the readability of the main clause improves. Otherwise
> use if-then-else.

A difference of emphasis here, but we actually agree. Finding it hard to assign a name to something is a good clue that you should not be splitting it out.

>> 5.17 Avoid assert and retract unless you actually need to preserve
>>     information through backtracking.
>>
>>     I would also say "If you have a dynamic predicate, write interface
>>     predicates for changing it instead of using 'bare' calls to
>>     assert and retract, so that your interface predicates can check
>>     that the data base will still be logically consistent after the
>>     change."
>
> These seems more complimentary than conflicting statements.

They are complementary, that's why I said "I would ALSO say"...

>> 6.12 Use write for debugging.
>>
>>     Wrong.  Use print for debugging; it was specifically intended for that.
>
> use debug(Channal, Format, Arguments). You can leave them in your code
> with no overhead if you compile using -O and they act as comments too.

Good advice for SWI Prolog.  The point I was making is that print/1 exists so that output can be tailored (and in particular, heavily and appropriately abbreviated) for debugging, while write/1 insists on showing you everything.  Combine the advice:  if you use debug/3, use the ~p format rather than the ~w format.

> True.  I strongly dislike files with commented dead code.  Only, use
> CVS or subversion :-)

For a single person project, SCCS or RCS are pretty much ideal. The CVS documentation has me completely baffled.

> Most of the time I put % TBD: whatever at the end of the line. Sometimes
> I simply call tbd('Whatever'). That will cause the system to raise an
> undefined predicate. Even better, list_undefined/0 will report them and
> where they are called.

The problem with comments like % TBD is that they are still there 4 years and 7 releases later.  tbd/1 and list_undefined/0 are excellent (albeit SWI-specific) advice.





From: Jan Wielemaker
Subject: Re: Good Examples of Properly Commented Prolog Code

We're getting close to consensus :-)

Richard A. O'Keefe wrote:
> Jan replied to my comments on Covington.
>     I like this very much, except I'd like to have a blank line between the
>     first semi-formal line and the description, starting with %
>
> When the comment starts out like this:
>      %   <name>(<mode><var>: <type>, ..., <mode><var>: <type>)
>      %   is true when .....
> the first line is the subject of the first sentence.
> It is really very jarring

I see.  I prefer to see it as a synopsis like the traditional Unix manpages. One of the reasons is  different patterns.  What about

%    age(+Name, -Age)
%    age(-Name, +Age)
%
%    ....

Are you going for

%    age(+Name, -Age)
%    is true if ...
%
%    age(-Name, +Age)
%    is true if ...

I also tend to document different arities in the same comment, like this:

%    rdf_load(+File)
%    rdf_load(+File, +Options)
%
%    ....

rdf_load(File) :-
    rdf_load(File, []).

rdf_load(File, Options) :-
    ....

> Perhaps we could advance this discussion further if Jan provided an
> example where he wanted to use _x _xx and _xxx and see what I can do
> about naming them.

I often call the do_something, or whatever.  The issue frequently arrises on constructs as below.  In this particular case load_stream/1 would be a good name, but there are enough cases where it is much harder to find a good name that isn't the same as the name of the main predicate.

load_file(File) :-
    open(File, read, In),
    call_cleanup(do_load_file(In), close(In)).

do_load_file(...)

>     I used to do that, but since the days of auto-indenting editors
>
> When I first heard about Emacs, I was ever so keen.
> When I actually got my hands on Emacs, it was a real disappointment.
> Emacs' auto-indentation for C was *horrible*, and it turned out to be
> impossible to reach a tolerable style by fiddling with its parameters.
> To this day, one of the first things I do with an autoindenting editor
> is switch automatic indentation *OFF*.

Properly trained, which unfortunately generally means sticking to the style used by the author of the indentation module or writing your own, auto-indenting editors are very valuable, as the fact that the indentation goes wrong is an immediate clue you've made a mistake. For me its 'can't program without them'. Again, its not about the number of keystrokes (although it helps a little), its about the feedback on errors.

> But I actually disagree.  I do NOT think "it should apply to any
> generator in a failure-driven loop".  There is something very very
> special about repeat/0 which does NOT apply to member/2 (at least
> in normal use), namely that repeat/0 never fails.  It has and is
> intended to have infinitely many solutions.  My layout for

Partly true, but in C I use the same layout for "for(;;)" and "for(init;cond;next)".  I don't see why this isn't the case in Prolog. They are equally special and dangerous.

> failure-driven loops is
>
>     (   member(X, List)
>         process(X),
>         fail ; true
>     ),
>
> just try and teach _that_ to an auto-indenter.  Note the signature

Works perfect in PceEmacs.  I used to use fail ; true this way.  Don't really know why I stopped doing it, maybe I should re-introduce it.

>     Unless you have I/O like (including graphics calls) predicates
>     ... I've seen _very_ long clauses, even overflowing the 512
>     subclause limit the system had very long ago (now unlimited).
>
> At Quintus we had a bug report from a customer:  they had a clause
> with more than 64000 variables!  It was machine-generated, of course.

Thats normal. Almost all limits the initial compiler had (#subclauses, #variables, size of produced code) had to be removed over time. The current compiler is limited by memory only, as most of the rest of the system.

>     > 4.11 Consider using a pretty-printer for finished printouts.
>
>     Might be a bit outdated.  Who prints programs these days with
>     nice large colour displays?
>
> Me.
>
>     Do get yourself a 1280x1024 or bigger monitor.
>
> I *have* a 1280x1024 monitor.  In fact both my SunBlade100 and my G4 Mac
> have such monitors.  You *still* don't get a whole lot of text on them,
> and if you want to really carefully read a file, paper is *wonderful*.
> I tend to find that syntax colouring just makes stuff less readable
> (because it reduces the contrast, which is well known to reduce
> readability).

I must say I find it hard these days to read Prolog without the colouring of PceEmacs.  Guess its what you're used to.  Just keyword colouring isn't very valuable in Prolog.

>>> 6.12 Use write for debugging.
>>>
>>>     Wrong.  Use print for debugging; it was specifically intended for
>>> that.
>>
>>use debug(Channal, Format, Arguments). You can leave them in your code
>>with no overhead if you compile using -O and they act as comments too.
>
> Good advice for SWI Prolog.  The point I was making is that print/1

Its easy enough to port the library to any system providing term_expansion or -bit less work- goal_expansion.

>> True.  I strongly dislike files with commented dead code.  Only, use
>> CVS or subversion :-)
>
> For a single person project, SCCS or RCS are pretty much ideal.
> The CVS documentation has me completely baffled.

You need very little for single person development. cvs add file, cvs rm file, cvs commit, cvs update and cvs diff cover 99% of what you need. Install web-browsing of the repository and you're all set and done.

>> Most of the time I put % TBD: whatever at the end of the line. Sometimes
>> I simply call tbd('Whatever'). That will cause the system to raise an
>> undefined predicate. Even better, list_undefined/0 will report them and
>> where they are called.
>
> The problem with comments like % TBD is that they are still there
> 4 years and 7 releases later.  tbd/1 and list_undefined/0 are excellent

True, but if the problem never became serious who cares?

> (albeit SWI-specific) advice.

Or any other system with a cross-referencer.




From: Uwe Lesta
Subject: Re: Good Examples of Properly Commented Prolog Code

I follow the discussion with a half eye.

Yes, Coding Guidelines are useful.

They are more useful if other tools like pretty_printer work with it.

but, IMO they will be commonly used if it is simple to write them and i will get immediately benefit of it.

A good sample for comments of methods are from ms visual studio The CSharp editor has templates for comments which are automatically inserted after typing '///' in the line befor a method definition.

and you get 'tooltips' shown by this comments if you point with the mouse on a method as well as you type the parameter list.

If you haven't seen it, try to get a lock on it.

My other point about no implicit conversion is this: If rationals stay rational, I don't think we need to worry about sort et. al being extended to support them.  Use predsort combined with compare_rationals/3, because mixed data will not be expected.



From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code

Jan Wielemaker wrote:
> I see.  I prefer to see it as a synopsis like the traditional Unix manpages.

Manpages are good for Manual PAGES.  That kind of layout is not designed for and not particularly good for SHORT comments.

> One of the reasons is  different patterns.  What about
>
> %    age(+Name, -Age)
> %    age(-Name, +Age)
> %
> %    ....
>
> Are you going for
>
> %    age(+Name, -Age)
> %    is true if ...
> %
> %    age(-Name, +Age)
> %    is true if ...

No.  ALL patterns should be TRUE in the same cases, otherwise why do they have the same name?  In that case, I'd leave the modes out of the first line and write

     %   age(Name, Age)
     %   is true when ....
     %   This may be used in modes (+,-) and (-,+) but no others.

> I also tend to document different arities in the same comment,

I don't.  They are different predicates.  They may be related, but they are different.

Note that we are talking here about the leading comment for ONE PREDICATE. If I want to say something about a GROUP of predicates, I have ANOTHER comment ahead of the group which describes the group.

>> But I actually disagree.  I do NOT think "it should apply to any
>> generator in a failure-driven loop".  There is something very very
>> special about repeat/0 which does NOT apply to member/2 (at least
>> in normal use), namely that repeat/0 never fails.  It has and is
>> intended to have infinitely many solutions.  My layout for
>
> Partly true, but in C I use the same layout for "for(;;)" and
> "for(init;cond;next)".

The less I say about your C layout, the better.  If I said what I really think about that, you would probably bar me from  the list for life.The thing is that in C it's the *same* construct:

    for (init; test; step) {
        body1;
        if (test2) break;
        body2;
        ...
    }

> I don't see why this isn't the case in Prolog.
> They are equally special and dangerous.

No, I have already explained why they are NOT equally dangerous.

> I must say I find it hard these days to read Prolog without the colouring
> of PceEmacs.  Guess its what you're used to.  Just keyword colouring isn't
> very valuable in Prolog.

I have seen, I have tried, keyword colouring in Prolog, and find that it distracts me from the content I need to see and makes the code nearly unreadable.  Prolog has so few keywords, and there is so little "key" about them, that there seems to me to be practically nothing gain by highlighting them.




From: Michael Maxwell
Subject: Re: Good Examples of Properly Commented Prolog Code

Richard A. O'Keefe wrote:
> I have seen, I have tried, keyword colouring in Prolog,=20
> and find that it distracts me from the content I need=20
> to see and makes the code nearly unreadable.  Prolog=20
> has so few keywords, and there is so little "key" about=20
> them, that there seems to me to be practically nothing=20
> gain by highlighting them.

The times I've been saved by my editor's coloring are times when I forgot to terminate a comment or a string.  I don't recall whether this was in Prolog or some other language, but it definitely helped find one class of errors.



From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code


Michael Maxwell wrote:
> The times I've been saved by my editor's coloring are times when I =
> forgot to terminate a comment or a string.

The funny thing is that nobody ever seems to cite any other ways that it helps.  There are other and arguably better tools for the job. For example, my editor includes a "Check Prolog syntax" command (Meta-_) which checks the syntax of the next clause.  It picks up unterminated comments by noticing /* inside a comment or by having too long a comment; it can pick up unterminated strings and atoms by noticing newlines inside strings that are not part of a continuation sequence.  It can *also* find a heck of a lot of other things that colouring will never notice.

The big thing about syntax colouring is that it tends to restrict you to having ONE syntax in a file.  If you like literate programming or have examples of one language inside another, you suffer.




From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code

I wrote:
> Perhaps we could advance this discussion further if Jan provided an
> example where he wanted to use _x _xx and _xxx and see what I can do
> about naming them.

Jan Wielemaker replied:
> I often call the do_something, or whatever.  The issue
> frequently arrises on constructs as below.  In this particular
> case load_stream/1 would be a good name, but there are enough
> cases where it is much harder to find a good name that isn't the
> same as the name of the main predicate.
>
> load_file(File) :-
>     open(File, read, In),
>     call_cleanup(do_load_file(In), close(In)).
>
> do_load_file(...)

I've been using Lisp long enough to find the name 'unwind_protect' more immediately comprehensible than 'call_cleanup', but never mind. Actually, no.  We SHOULD mind.  Because if we use the name 'unwind_protect' there is an immediately obvious name for the subordinate predicate:

    %   load_file(*File: file_name)
    %   is given a file name and is responsible for loading it.
    %   Whatever happens, the stream should not be left open.

    load_file(File) :-
        open(File, read, In),
        unwind_protect(protected_load_stream(In), close(In)).

    %.  protected_load_stream(*In: input_stream)
    %   is given a stream open for reading; it loads forms
    %   from that stream.  It is the responsibility of the
    %   caller to close the stream; this predicate may raise
    %   exceptions to report problems.

    protected_load_stream(In) :-
        ...

Here there are two changes to the name:

  1. load_<<file>> becomes load_<<stream>>, because the one is given a file name, and the other is given a stream (which need not ever have had anything to do with a file, so ...load_file... would be a bad name for it.
  2. <<>>load_stream becomes <<protected_>>load_stream to make it clear that this should only be called in the scope of an unwind_protect/2  that will do whatever cleanup is necessary. If you insist on call_cleanup/2 (which I think is a bad name because it is not the *call* that needs a cleanup action but the *exit, failure, or exception*) then you could turn  load_stream<<>> into load_stream<<_no_cleanup>>.
> Properly trained, which unfortunately generally means sticking to the
> style used by the author of the indentation module

Yes, but authors of indentation modules seem to come up with such execrably bad indentation styles.

> or writing your own,

I've tried that too.  The thing that I found when I did it is that RIGID INDENTATION STYLES ARE WRONG.  For example, it often pays to lay things out in a tabular style.  Auto-indenters destroy that. In fact, that's one of the standing complaints about indent(1).

To give a trivial example,

    struct {
        char *name;
        int   day;
        int   month;
    } birthday[] = {
    {"john",      1,  2},
    {"henry",    27,  3},
    {"sue",       7,  4},
    {"maryanne", 10, 11}
    };

is turned into

    struct {
    char *name;
    int day;
    int month;
    }      birthday[] = {

    {
        "john", 1, 2
    },
    {
        "henry", 27, 3
    },
    {
        "sue", 7, 4
    },
    {
        "maryanne", 10, 11
    }
    };

which looks horrible and goes out of its way to put the variable name in a bad place.

> auto-indenting editors are very valuable,

I do not call tools that systematically destroy valuable layout clues for the sake of enforcing half a style blindly "valuable".

> as the fact that the
> indentation goes wrong is an immediate clue you've made a mistake.

I repeat an earlier observation:  this ONLY works if you are writing a SINGLE language in a source file and you are NOT using a literate programming tool.  And my experience with Emacs in several languages has been that the indentation going wrong is almost always an artefact of Emacs getting it wrong and has nothing to do with any errors of mine.

Typically, after I've been editing Prolog for about half an hour, I'll use my editor's built-in Prolog syntax check to look for typos. This finds the mistakes that auto-indentation might have found (but probably wouldn't) *and* a lot more (such as singleton variable errors). One great thing about this is that I can limit the check to the part of a buffer that *is* Prolog.  I can check Prolog clauses embedded in LaTeX documents or embedded in C comments as easily as I can check Prolog in files that are nothing but Prolog.

>> The problem with comments like % TBD is that they are still there
>> 4 years and 7 releases later.  tbd/1 and list_undefined/0 are excellent
>
> True, but if the problem never became serious who cares?

Someone trying to maintain the program. How do you *know* the problem never became serious? Maybe the people with the problem just gave up.

Right now I am reading a book about a rather exciting area of statistics. It was formatted in LaTeX, and *all* the chapter references in the text are "chapter TEX.NAME" (where TEX.NAME is some internal tag in capitals and maybe some punctuation) instead of to chapter numbers.  It's a real pain.  (Just in case it's not obvious, the internal tags are not the same as nor are they abbreviations of the chapter titles).  This is a published hardback book.

>> (albeit SWI-specific) advice.
>
> Or any other system with a cross-referencer.

The name of the command is SWI-specific.  That's what I meant.




From: Jan Wielemaker
Subject: Re: Good Examples of Properly Commented Prolog Code

Instead of filling this forum, it would be more valuable to enhance the original style guide ...  I've sent a message to Michael who doesn't appear to be on the mailinglist anymore (or he changed address).

Richard A. O'Keefe wrote:
> I've been using Lisp long enough to find the name 'unwind_protect'
> more immediately comprehensible than 'call_cleanup', but never mind.
> Actually, no.  We SHOULD mind.  Because if we use the name
> 'unwind_protect' there is an immediately obvious name for the
> subordinate predicate:

call_cleanup/2 was introduced by SICStus (at least thats where I found it first). I prefer to keep the name compatible.

>>load_file(File) :-
>>    open(File, read, In),
>>    unwind_protect(protected_load_stream(In), close(In)).
>
> Here there are two changes to the name:
>
> (1) load_<<file>> becomes load_<<stream>>, because the one is given a

I already suggested that for this case

> (2) <<>>load_stream becomes <<protected_>>load_stream to make it clear
>     that this should only be called in the scope of an unwind_protect/2
>     that will do whatever cleanup is necessary.

Actually this isn't correct. You're not going to call write/2 write_protected/2, I may hope. load_stream/1 is a perfectly valid predicate and like all I/O predicates working on streams doesn't close the stream itself and is capable of generating errors.  Actually almost any predicate is capable of generating resource errors, and almost all code needs to be protected at some level against this possibility.

_protected or whatever is generally a good idea for with_mutex(+Mutex, :Goal), in which case it is generally not allowed to call the `protected' code directly and this is not immediately obvious.

>>or writing your own,
>
> I've tried that too.  The thing that I found when I did it is that
> RIGID INDENTATION STYLES ARE WRONG.  For example, it often pays to
> lay things out in a tabular style.  Auto-indenters destroy that.
> In fact, that's one of the standing complaints about indent(1).

Indent is good if you receive code written in a style you don't like at all and wish to reuse/examine/... it.  The result is generally not perfect, but at least more easy to read than the original.  Syntax support in an editor is different as, while writing, you have the option to overrule the editor.  Indeed, quite a few of the style rules have exceptions.

>>auto-indenting editors are very valuable,
>
> I do not call tools that systematically destroy valuable layout clues
> for the sake of enforcing half a style blindly "valuable".

It it was `half a style' I'd agree. For Prolog I follow PceEmacs probably about 99% of the time.  Ok, I wrote it myself ...

>>as the fact that the
>>indentation goes wrong is an immediate clue you've made a mistake.
>
> I repeat an earlier observation:  this ONLY works if you are writing a
> SINGLE language in a source file and you are NOT using a literate
> programming tool.  And my experience with Emacs in several languages has
> been that the indentation going wrong is almost always an artefact of Emacs
> getting it wrong and has nothing to do with any errors of mine.

Emacs Prolog mode is pretty poor. Its written by a Lisp programmer :-) Its not all that difficult to train an editor to recognise multiple contexts in one file.  Of course this only applies to a literal programming system.  The few times I write Prolog code in C files I can live without indentation support or switch to Prolog mode and back.

>>> The problem with comments like % TBD is that they are still there
>>> 4 years and 7 releases later.  tbd/1 and list_undefined/0 are excellent
>
>> True, but if the problem never became serious who cares?
>
> Someone trying to maintain the program.

He'll generally be more happy with a TBD remark, indicating where and what the original author thought something should have been done, than nothing at all.

> How do you *know* the problem never became serious?
> Maybe the people with the problem just gave up.

Thats possible. SWI-Prolog and all its libraries is full of TBD issues. Most of them never hurt. Quite more often there are issues not flagged as TBD that cause people to react :-)



From: Jan Wielemaker
Subject: Re: Good Examples of Properly Commented Prolog Code


Jan Wielemaker wrote:
> Instead of filling this forum, it would be more valuable to enhance the
> original style guide ...  I've sent a message to Michael who doesn't
> appear to be on the mailinglist anymore (or he changed address).

I've received the LaTeX source from Michael. We are allowed to modify it, as long as he remains author of course. One plan could be to incorporate the results of the discussion, add SWI-Prolog specific concerns and add it as a chapter to the SWI-Prolog manual.  Other plans?  Someone willing to invest some time?




From: Roberto Bagnara
Subject: Re: Good Examples of Properly Commented Prolog Code


Jan Wielemaker wrote:
> On Thursday 16 March 2006 17:33, Jan Wielemaker wrote:
>> Instead of filling this forum, it would be more valuable to enhance the
>> original style guide ...  I've sent a message to Michael who doesn't
>> appear to be on the mailinglist anymore (or he changed address).
>
> I've received the LaTeX source from Michael. We are allowed to modify
> it, as long as he remains author of course. One plan could be to
> incorporate the results of the discussion, add SWI-Prolog specific
> concerns and add it as a chapter to the SWI-Prolog manual.  Other
> plans?  Someone willing to invest some time?

I am.  Actually, my original plan was to write a document that could serve as a basis for the current development efforts of my group. I would have submitted that document to this list for further comments. Of course, I would have included some of "my personal habits". However, another possibility is to first improve Michael's manuscript trying to be as objective as possible: the issues of personal taste can always be added at a later stage and the intermediate document can be useful to the community.




From: Bart Demoen
Subject: Re:Good Examples of Properly Commented Prolog Code

> as a chapter to the SWI-Prolog manual.  Other
> plans?  Someone willing to invest some time?

I would just suggest that one doesn't present "my personal habits" as general advice to others. There was a lot of that in the recent "discussion".



From: Jan Wielemaker
Subject: Re: Good Examples of Properly Commented Prolog Code

Bart Demoen wrote:
>> as a chapter to the SWI-Prolog manual.  Other
>> plans?  Someone willing to invest some time?
>
> I would just suggest that one doesn't present "my personal habits" as
> general advice to others. There was a lot of that in the recent
> "discussion".

I do not agree. Ok, coding style has a personal element. I do see a lot of poor style and I'm pretty sure it will help if there is good document to point to that explains what (a) good style is. Even well written code that is not in _your_ style is generally hard to read, which make it desirable to share one style in a community. I think a large majortity of the statements from Michael's document and the remarks is not just a matter of personal taste.




From: Simon Price
Subject: Re: Good Examples of Properly Commented Prolog Code


Guidelines are always a mixture of "what's good for everyone" and "what's good for me" but the hope is that there will be more convergence on the personal habits side so that it is easier to read/maintain each others' code. In many cases it doesn't matter what The Style is so long as people use it. The Java community, for example, has benefited from a well thought out (mostly) style document that came out with the language. Editors do have a part to play but are much more a matter of personal choice or an accident of personal history. Personally I really appreciate the sharing of ideas on Prolog style - even the habits.



From: Wlngg
Subject: Re: Good Examples of Properly Commented Prolog Code


... Just to mention one dimension which has been neglected so far in the discussion: the non-English programming world.

With the arrival of Unicode, programming languages (e.g. Java), following "global" applications (e.g. Word), have made sometimes considerable efforts to allow localisation.

Could or Should SWI-Prolog follow in the footsteps of Java? May be not right now. That SWI-Prolog allows now the possibility of programming on the basis of the Unicode encoding is in itself a great improvement. The terse syntax of Prolog should only make the main task of adapting the programming environment to different cultural contexts easier.

Stylistic considerations, while dependent on the cultural context, are very important for communications purposes. What I would like to see more of is the increased possibility for programming languages to be customisable to the specific needs of the cultural and linguistic contexts of the programming community, or alternatively, to be generic enough to cater for different cultural and linguistic contexts (the second alternative being the one I personally favour). For example, LPA Prolog and even Strawberry Prolog allow predicate names in "any" script (I have tried it with the first one).

Prolog has a great advantage over languages like Java or C ..., through its minimalist syntax. Except for built-ins and the few reserved words, the rest is already pretty international. This should allow for greater flexibility in Prolog.

To illustrate my point about the need for improvement, take the case of variables' notation in Prolog : the rule that variables start with uppercase letters among other (PrologIII takes lower case letters for variables and upper-case for constants, but a primed upper-case like C' becomes a variable, etc.) is good for languages that have the lower/uppercase differentiation; many scripts don't have such a differentiation. We could do with a more "global" or generic syntax for variables, and for other aspects of the Prolog language, ... after all, Prolog is about programming "in logic".




From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code

Simon Price wrote:
> Guidelines are always a mixture of "what's good for everyone"
> and "what's good for me" but the hope is that there will be more
> convergence on the personal habits side so that it is easier to
> read/maintain each others' code.

The Ada Quality and Style Guidelines are a shining model of how to do it well.  One example of that concerns identifier casing:  Ada is not case sensitive, so tom_paine, Tom_Paine, TOM_PAINE, toM_painE and so on all mean the same.  The first edition of the AQ&S guidelines had one recommendation.  Current editions have a *different* recommendation, with the remark that experience proved the original recommendation didn't work as well as they hoped.

It is possible to present style guidelines in a "patterns" sort of way:
In this case, the specific recommendation might have a "what's good for me" element, but we may with some reason hope that the rest of the material remains useful even for people who think they have a better answer.

> In many cases it doesn't matter what The Style is so long as
> people use it.  The Java community, for example, has benefited
> from a well thought out (mostly) style document that came out
> with the language.

Well, no, it *didn't* come out with the language.  One of my students at RMIT developed a style guide for Java *before* the official one came out.  (Versions of Java *were* available before 1.0.)





From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code


Wlngg raises the issue of internationalisation.

  1. Quintus Prolog allowed "any script" back in the late-80s. My proposal to the ISO Committee dealt with this clearly and in some detail; like most of my proposals it was completely ignored.
  2. I don't know what SWI Prolog does with unquoted identifiers, but it can certainly handle any characters in quoted atoms.
  3. Concerning variables, the solution which was adopted back in the mid-80s was ever so simple:  for scripts that do not have (or, in the case of Georgian, do not use) a lower-case/upper case distinction, variables just begin with "_".  This means that the singleton variable check has to be modified slightly:
So _Fred is a named singleton, _(Hanzi) is an ordinary variable where (Hanzi) is a Chinese character, and __(Hanzi) is a named singleton using a Chinese character.
This preserves the rule "stick an underscore in front of a variable name to keep it out of singleton warnings".

Like I said, this is a problem that was solved back in the late 80s.




From: Wlngg
Subject: Re: Good Examples of Properly Commented Prolog Code


Richard O'Keefe replied:
> (1) Quintus Prolog allowed "any script" back in the late-80s.
>     My proposal to the ISO Committee dealt with this clearly and
>     in some detail; like most of my proposals it was completely ignored.

With Unicode available now (contrary to the 80s), your proposals should no longer remain ignored. For some of us they are of an urgent necessity.  In the draft document "An Elementary Prolog Library" (pllib.htm) Richard O'Keefe wrote the following statement: "In order to deal effectively with Unicode (and it was the plain responsibility of the ISO Prolog committee to address this), some other means entirely will have to be found." If that is still the case, then SWI-Prolog could provide some of those means without compromising Prolog's integrity. I think the key principle is that of genericity: if the satisfaction of a new and specific requirement leads to a greater genericity of the underlying Prolog syntax, then it should be upheld

I could list a dozen other individual and specific requirements Prolog could satisfy: programmers with SOV (Subject Object Verb) natural languages, would wish to be able to write the predicate

    is_father_of(abraham,isaac) ...

as

    (abraham,isaac)father_of_is ...

further more, in their own script.

Whether or not these new requirements compromise the integrity of Prolog or entail peformance degradations, it is for the language designers and compiler writers to decide and accommodate.

In any case, I believe that greater syntactic genericity is the way ahead for Prolog (or any other language for that matter.)



From: Jan Wielemaker
Subject: Re: Good Examples of Properly Commented Prolog Code

Richard A. O'Keefe wrote:
> (2) I don't know what SWI Prolog does with unquoted identifiers,
>     but it can certainly handle any characters in quoted atoms.

It follows the rules of Prolog, assuming there are no `singletons' outside the ASCII range. So, an unquoted atom is a lowercase followed by a sequence of letter|digit or a sequence of punctuation characters. A variable is uppercase or _ followed by letter|digit. The classification is left to iswupper(), etc. of the C-library. Thats not good, mainly because C libraries differ seriously in how they implement iswupper() and friends. Notably some only provide meaningful results for the `installed languages', while others provide support regardless of installed languages for a smaller or larger subset of the unicode range. The big problem is that it depends on the C-library and/or installed languages whether a program can be loaded on another machine.

So, I think I should include tables in Prolog that do the classications we need and use that in the parser.  Its on my todo list.  It isn't a very big issue and it is on my TODO list.  Would be good to have testers willing to provide active feedback, preferable with some knowledge on coding issues.




From: Bart Demoen
Subject: Re: Good Examples of Properly Commented Prolog Code

> programmers with SOV (Subject Object Verb) natural languages, would=
>
> wish to be able to write the predicate
>
>    is_father_of(abraham,isaac) ...
>
> as
>
>    (abraham,isaac)father_of_is ...

If you define father_of_is as a postfix operator, you have already (part of) what you want.

But I realise this is an unsatisfactory answer - I myself hate answers that tell me that "I can do it myself" because I am of course as lazy as any other person :-)

So I will give you an even more unsatisfactory answer ...

> In any case, I believe that greater syntactic genericity is the way
> ahead for Prolog (or any other language for that matter.)

In the 80-ies, several languages tried the road of "redefinable syntax" or "user-definable syntax". My first dabbling with Prolog implementation involved exactly that. That road didn't become the main language design road - I am not sure why exactly, but the more flexibility in the syntax, the more difficult it is to let others read your program. Prolog still allows you to (re)define the operators - which can make a program totally unreadable to anyone not familiar with your declarations. Mercury is more rigid in that respect. Cobol was an attempt at a syntax closer to natural language - that part of Cobol wasn't that much a success.

If you really want to program in a different syntax, but with an X-like semantics (X being Prolog at this point I presume), I would say that you should do your preprocessor yourself, and not try to bend the X-syntax: there are just too many potential syntax bending ways.





From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code

I wrote:
> (1) Quintus Prolog allowed "any script" back in the late-80s.
>     My proposal to the ISO Committee dealt with this clearly and
>     in some detail; like most of my proposals it was completely ignored.

Wlngg wrote:
> With Unicode available now (contrary to the 80s), your proposals should no
> longer remain ignored. For some of us they are of an urgent necessity.

In the 1980s we did not have Unicode, true. But we DID have
And there was the ISO 2022 framework as well. So we DID have the problem of dealing with large character sets (we had a Japanese terminal and tested the Japanese support in-house). What we DIDN'T have was a *single* large character set to support.

In fact, one of the earliest documents in the catalogue of documents distributed to the standardisers, PS/6, already talked about the need to support large character sets back in 1984 (or maybe '83; I don't remember when I wrote it).

> I could list a dozen other individual and specific requirements
> Prolog could satisfy: programmers with SOV (Subject Object Verb)
> natural languages, would wish to be able to write the predicate
>
>    is_father_of(abraham,isaac) ...
>
> as
>    (abraham,isaac)father_of_is ...
>
> further more, in their own script.

I note that Prolog's normal order, Verb Subject Object, does NOT match the typology of the languages spoken by Prolog's designers (which are SVO).

There is no question that programmers should be able to use their own script.  I firmly believe that programmers should be able to write numbers in their own script as well as identifiers (my lexical proposal to the ISO committee allowed this).  In fact, I see no reason why I shouldn't be allowed to write 2(1/2) using the Latin-1 (1/2) character.

As for their own syntax, I don't think it is reasonable to ask a standard to support every variation (all six permutations of SVO, pro-drop or non-pro-drop, switch reference, ...) that the world's natural languages exhibit.  But there _is_ a case to be made for allowing people to plug in their own parser.  One standard syntax plus one standard way to plug in your own parser means that someone who wants (x,y)p can write a parser for that once.

Pluggable parsers for the compiler would of course have no *runtime* performance implications.





From: Wlngg
Subject: Re: Good Examples of Properly Commented Prolog Code

Bart Demoen wrote:
> If you define father_of_is as a postfix operator, you have already
> (part of) what you want.

... is the xyf mode possible in SWI-Prolog?


> In the 80-ies, several languages tried the road of "redefinable
> syntax" or "user-definable syntax". My first dabbling with Prolog
> implementation involved exactly that. That road didn't become the main
> language design road - I am not sure why exactly, but the more
> flexibility in the syntax, the more difficult it is to let others read
> your program. Prolog still allows you to (re)define the operators -
> which can make a program totally unreadable to anyone not familiar
> with your declarations.

Well, in the 80s many people were (rightly) happy with 7-bit encoding. It was also in the 70s-80s that we had the "Edinburgh"/"Marseilles" Prologs: PrologIII still offers both styles. Unicode brings with it many more challenges that will certainly be to the great advantage of Prolog: its syntax should, ultimately cater for most needs, with the possibility of reverting any specific form back to the "standard" form (as it has been possible in the past, to switch from the "Marseilles" to the "Edinburgh" Prolog and vice-versa). The latter could be considered an aberration; whereas the first is a necessity: the Arabic programmers would use a right-to-left style in their own script, as well as the Hebrew programmers, etc. Each programming community should have no difficulty communicating among itself, or with the wider Prolog community. What matters is that they are all programming "in Prolog". Having said that, I do appreciate the complexities involved, and the required changes in attitude. All in good time ... For now what is really urgently required is the ability to program in Prolog, with the Latin as well as the non-Latin scripts.

Richard O'Keefe wrote:
> There is no question that programmers should be able to use their
> own script. I firmly believe that programmers should be able to
> write numbers in their own script as well as identifiers (my lexical
> proposal to the ISO committee allowed this). [...] As for their own
> syntax, I don't think it is reasonable to ask a standard to support
> every variation (all six permutations of SVO, pro-drop or
> non-pro-drop, switch reference, ...) that the world's natural
> languages exhibit. But there _is_ a case to be made for allowing
> people to plug in their own parser.

Thanks Richard and Bart. I leave you with a greater satisfaction that Prolog is in good hands!






From: Roberto Bagnara
Subject: Re: Good Examples of Properly Commented Prolog Code

Jan Wielemaker wrote:
> Bart Demoen wrote:
>>> as a chapter to the SWI-Prolog manual.  Other
>>> plans?  Someone willing to invest some time?
>> I would just suggest that one doesn't present "my personal habits" as
>> general advice to others. There was a lot of that in the recent
>> "discussion".
>
> I do not agree. Ok, coding style has a personal element. I do see a lot
> of poor style and I'm pretty sure it will help if there is good document
> to point to that explains what (a) good style is. Even well written code
> that is not in _your_ style is generally hard to read, which make it
> desirable to share one style in a community. I think a large majortity
> of the statements from Michael's document and the remarks is not just a
> matter of personal taste.

Simon Price wrote:
 > Guidelines are always a mixture of "what's good for everyone" and
 > "what's good for me" but the hope is that there will be more convergence
 > on the personal habits side so that it is easier to read/maintain each
 > others' code. In many cases it doesn't matter what The Style is so long
 > as people use it. The Java community, for example, has benefited from a
 > well thought out (mostly) style document that came out with the
 > language. Editors do have a part to play but are much more a matter of
 > personal choice or an accident of personal history. Personally I really
 > appreciate the sharing of ideas on Prolog style - even the habits.

I agree with Jan and Simon.  Moreover, as long as the personal habits come along with their rationale, they can be weighted against each other and all will help to improve the state of things (which is, as Jan says, dominated by poor style, starting from my own sources).

Concerning coding guidelines that could have an impact on the community, I think they should present both the basic and the more sophisticate techniques.  For example, it is clear that

     <name>(<mode><var>: <type>, ..., <mode><var>: <type>)

is a very good way to begin the documentation of a predicate. Especially if one uses the extended set of modes indicated by Richard (instead of the simpler and less expressive {+, -, ?}). And it is even better if <type> is specified in a formal way, perhaps in a way that enables the use of automatic type checkers... However, since the perfect is the enemy of the good, I believe we should not put things this way.  It is probably more productive to start with a "basic level" where types can be omitted and where the mode system is the simpler one and then explain why the more complex mode system is strictly superior and why specifying types is beneficial.  I believe this way it would be easier to have convergence on the basic level.  If we make sure the more advanced levels are extensions of the more basic ones and we succeed in explaining why the more advanced techniques are worth their higher cost, then I believe many people will adopt the basic level (which is already a nice thing) and, being conscious of its limitations, some of them will gradually move to more advanced levels.  People working on big project may instead decide to enforce the more advanced guidelines from the beginning.





From: Paulo Moura
Subject: Re: Good Examples of Properly Commented Prolog Code


Richard A. O'Keefe wrote:
>     = (not in Covington)    not necessarily ground, but not further bound
>     so for example it is simply wrong to write
>     compare(?R: order, +T1: term, +T2: term)
>     because T1 and T2 are allowed to be variables, and
>     compare(?R: order, ?T1: term, ?T2: term)
>     is misleading because we expect that ? arguments will be unified
>     with something, but
>     compare(?R: order, =T1: term, =T2: term)
>     is just right.

The Prolog ISO standard (Part I) uses "@" with the same meaning (section 8.1.2.2; page 64). Thus, compare/3 would be specified as:

    compare(?order, @term, @term)





From: Richard O'Keefe
Subject: Re: Good Examples of Properly Commented Prolog Code

Paulo Moura wrote about "extended" modes:
> The Prolog ISO standard (Part I) uses "@" with the same meaning
> (section 8.1.2.2; page 64). Thus, compare/3 would be specified as:
>
>     compare(?order, @term, @term)

I keep the ISO Prolog substandard on-line, but I never expect to find any good ideas in it. The "@" notation has an obvious relationship to the term comparison predicates @< and so on. That makes '@' arguably more intention-revealing than '='. Noted.






From: Roberto Bagnara
Subject: Re: Good Examples of Properly Commented Prolog Code

Richard A. O'Keefe wrote:
> Steve Moyle wrote:
>> One place to start would be Covington's guidlines:
>>
>> [PDF] Some Coding Guidelines for Prolog
>> http://www.ai.uga.edu/mc/plcoding.pdf
>
> I have a few quibbles with that.
>
> [...]
>
> 6.6 Test every predicate by forcing it to backtrack.
>
>     Misleadingly worded:  many predicates *can't* backtrack.
>     "Test every predicate by failing back into it"; now that's
>     something you *can* do.

I would go a bit further here, since the verb "test" alone does not mean much.  I would give the explicit advice of checking that failing back into the predicate _really__does_ (as apposed to _apparently_does_) the right thing.  For example, especially (but not only) when the predicate at hand is meant to be deterministic or semideterministic, checking that choicepoints left around are as expected is often the source of interesting surprises.  I believe the paragraph about the importance of mastering the debugger should explicitly mention this: I met several people (and I am not talking only about students) that were completely unsuspecting of the possibility of obtaining a list of pending alternatives.

> 6.7 Test predicates by supplying arguments of the wrong types.
>
>     I'm not so sure about this one.  For a beginner, it may be useful
>     advice so that they find out what is likely to provoke which
>     error message.  But append/3, for example, is designed to work
>     with lists; it isn't *intended* to do anything in particular if
>     given arguments of any other type, so there is no sense in which
>     you can test it for other types.  (To test something, there must
>     be a specified set of acceptable behaviours so that you can tell
>     whether the actual behaviour is acceptable or not.  If "anything
>     goes", then the "test" cannot fail.)

Perhaps it means: "When a predicate is part of a public interface, make sure it fails straightaway when arguments do not satisfy the requirements set by the interface (instead behaving in a bizarre and completely unpredictable way."  This advice is good for any language. But I believe for a language such as Prolog is particularly good: systematic checking of the interfaces has saved me lot of headaches. My 2 cents, of course.

Noted.





From: Paul Singleton
Subject: Re: Good Examples of Properly Commented Prolog Code


Roberto Bagnara wrote:
>> 6.6 Test every predicate by forcing it to backtrack.

> ...I would give the explicit advice of checking that
> failing back into the predicate _really__does_ (as apposed to
> _apparently_does_) the right thing.  For example, especially (but
> not only) when the predicate at hand is meant to be deterministic
> or semideterministic, checking that choicepoints left around are as
> expected is often the source of interesting surprises.

Is there a reliable programmatic way of checking whether a goal leaves a choicepoint?  e.g.

   succeeds_without_choicepoint(Goal) :-
       statistics(localused, L1),
       call(Goal),
       statistics(localused, L2),
       L1 >= L2.

   ?- succeeds_without_choicepoint(member(1, [1,2,3])).

   No
   ?- succeeds_without_choicepoint(memberchk(1, [1,2,3])).

   Yes

but I guess all sorts of things could go wrong with this?



Back to top
Nested Predicates?
From: Maurizio Colucci


Whenever I realize that a predicate is only used inside another one, I miss the ability to nest predicates. It would make the code more readable: instead of seeing a flat space, the reader would only see a few top-level predicates, and make his way into the code in a hierarchical way, like traversing a tree.

Is there any plan for such an addition to SWI? Is there any standard?
Is there any counter-argument or alternative solution? Thanks





From: Paulo Moura
Subject: Re: Nested Predicates?

You may use SWI-Prolog module system or Logtalk objects to encapsulate auxiliary predicates and expose only a "few top-level predicates" as you write above.



From: Jan Wielemaker
To: SWI-Prolog mailing list
Subject: Re: [SWIPL] Nested predicates?
Date: Sun, 26 Mar 2006 17:44:26 +0100

Maurizio Colucci wrote:
> Whenever I realize that a predicate is only used inside another one, I
> miss the ability to nest predicates. It would make the code more
> readable: instead of seeing a flat space, the reader would only see a
> few top-level predicates, and make his way into the code in a
> hierarchical way, like traversing a tree.
>
> Is there any plan for such an addition to SWI? Is there any standard?

I think the answer is 'no no'.

> Is there any counter-argument or alternative solution? Thanks

As suggested, modules (or objects) are the standard way around. As for counter arguments, I think is needlessly complicates syntax and would require various new primitives to examine the program, do meta-interpretation, etc. If there is something to be nested, modules come to mind as the first candidate. The currently flat module space is getting a problem as the amount of reusable code grows, especially if people aren't careful to use some kind of prefix to avoid name-clashes.




From: Richard A. O'Keefe
To: SWI-Prolog mailing list
Subject: Re:  [SWIPL] Nested predicates?
Date: Mon, 27 Mar 2006 15:34:55 +1200 (NZST)

Maurizio Colucci wrote:
> Whenever I realize that a predicate is only used inside another one,
> I miss the ability to nest predicates.

When I started writing Prolog code at Edinburgh, I used to indent auxiliary predicates.  Taking the example of calculating Fibonacci numbers, which happened to come up in another mailing list, I would have written

    :- mode fib(+, ?).


    fib(0, 1) :- !.
    fib(N, F) :-
    integer(N), N > 0,
    fib(N, 1, 1, S),
    F = S.

    :- mode fib(+, +, +, -).

    fib(1, X, _, X) :- !.
    fib(N, X, Y, S) :-
        N1 is N - 1,
        W is X + Y,
        fib(N1, W, X, S).

Lawrence Byrd looked at some of my code, and said "You really miss Algol, don't you?"

> It would make the code more readable:

Once I got used to the fact that Prolog really isn't that kind of  language, I realised that no, it *didn't* make the code more readable.

> instead of seeing a flat space, the reader would only see a
> few top-level predicates, and make his way into the code in a
> hierarchical way, like traversing a tree.

This is one of the reasons.  Things are like a tree much less often than one thinks.  An auxiliary predicate having been defined, you often find uses for it elsewhere.  (For example, my fib/3 can also be used to calculate Lucas numbers.)

> Is there any plan for such an addition to SWI?

I hope not.

> Is there any standard?

Yes.  The standard is "don't do that".

> Is there any counter-argument or alternative solution?

The readability of your code depends far more on the quality of your comments than on nesting.  I don't really believe that there is any problem here that needs a solution.  The way to encapsulation stuff is to use modules.

Oddly enough, I've recently been suggesting child modules for another similar language in another mailing list.  Child modules would be nice for very large systems.  Curiously enough, I know of two languages that *had* hierarchical modules (SETL and Lisp) which dropped them (ISETL and SETL2 have a flat module name space, as does Common Lisp).



From: Richard A. O'Keefe
To: SWI-Prolog mailing list
Subject: RE: [SWIPL] Nested predicates?
Date: Tue, 28 Mar 2006 14:07:09 +1200 (NZST)

Ralf Lammel wrote, rather strangely,
> I don't see any convincing argument in Richard O'Keefe's email that
> explains why "nested predicates" in Prolog are *conceptually* less
> sensible than local function definitions in Algol, Haskell, Pascal, ...

That's because I didn't even *try* to provide such an argument, and that's because I did not make any such claim.

I did not say, sing, whistle, hum, inscribe on clay, stone, sand, wax, or any other material, transmit by Morse code, mental telepathy, pheromones, or any other means, nor cause nor induce any other person to do so, any statement which could be held as meaning or implying that "nested predicates are conceptually less sensible in Prolog".

They aren't *necessary*.
They would greatly complicate the language.
They would greatly increase the difficulty of writing tools to process the language.
Using Prolog since October 1979 has shown me that they would have VERY low payoff.

But none of that says that they are "conceptually less sensible".

Of *course* you could have a logic programming language with nested predicates, and if you want one, by all means go ahead and design and implement it.  Just don't expect anyone else to pay the price.

> Richard says that "Things are like a tree much less often than
> one thinks."  but how does this explain that local definitions
> are used quite a bit in say Haskell

As it happens, between the time I wrote this sentence and the time I'll write the next one, I'm going to give a 2 hour lecture on Haskell programming [left at 10:54am]. [returned at 1:06pm] Can I make the obvious point that "Things are like a tree much less often than one thinks" was a statement about Prolog, so cannot fairly be expected to explain anything about Haskell?

Let me also make the obvious point that Haskell *needs* local definitions because it doesn't have unification.  Consider the following Haskell code for computing the "percentage bend correlation":

pbcor = pbcor_gen 0.1

pbcor_gen beta pairs =
    dot as bs / sqrt (dot as as * dot bs bs)
    where
    dot xs ys = sum [x*y | (x,y) <- zip xs ys]
    as = scaled_and_clamped xs
        bs = scaled_and_clamped ys
        (xs, ys) = unzip pairs

        scaled_and_clamped xs =
            [(0-1) `max` (1 `min` ((x-phi)/omega)) | x <- xs]
            where
        phi = (omega*fromIntegral (i2-i1) + s)/fromIntegral (n-i1-i2)
                s = sum ((n-i1-i2) `take` (i1 `drop` sorted_xs))
                sorted_xs = sort xs
        i1 = length [1 | x <- xs, (x-median)/omega < -1]
        i2 = length [1 | x <- xs, (x-median)/omega > 1]
                omega = sort ws !! (floor ((1 - beta)*fromIntegral n) - 1)
                ws = [abs (x - median) | x <- xs]
                median = (sorted_xs !! h + sorted_xs !! (n-1-h)) / 2
                h = n `div` 2
                n = length xs

In this Haskell code, there are
     2 nested functions
    14 nested variables
     5 list comprehensions

What does a Haskell compiler do with the nested functions? IT MOVES THEM OUT!  (This is called 'lambda lifting', IIRC.) We can do the same for Prolog.

pbcor(Pairs, R) :-
    pbcor(Pairs, 0.1, R).

pbcor(Pairs, Beta, R) :-
    unzip(Pairs, Xs, Ys),        % no let/where is needed for
    scaled_and_clamped(Xs, Beta, As),   % introducing Xs, Ys, As, or Bs.
    scaled_and_clapmed(Ys, Beta, Bs),   % (nor AA, AB, or BB).
    dot(As, As, AA),
    dot(As, Bs, AB),
    dot(Bs, Bs, BB),
    R is AB/sqrt(AA*BB).

scaled_and_clamped(Xs, Beta, Ys) :-
    length(Xs, N),            % once again, there is no need
    H1 is N // 2,            % for any let/where just so that
    H2 is N - 1 - H1,            % we can define local variables!
    msort(Xs, Sorted_Xs),
    nth0(H1, Sorted_Xs, Mid_Lo),
    nth0(H2, Sorted_Xs, Mid_Hi),
    Median is (Mid_Lo + Mid_Hi)/2,
    Median_Shift is -Median,
    scaled_and_shifted(Xs, 1, Median_Shift, Ms),
    abs_list(Ms, Ws),
    msort(Ws, Sorted_Ws),
    Omega_Pos is floor((1 - Beta)*N) - 1,
    nth0(Omega_Pos, Sorted_Ws, Omega),
    count(X, Ms, X < -Omega, I1),
    count(X, Ms, X >  Omega, I2),
    I3 is N - I1 - I2,
    drop(I1, Sorted_Xs, Mid_And_High_Xs),
    take(I3, Mid_And_High_Xs, Mid_Xs),
    sum(Mid_Xs, S),
    Phi is (Omega*(I2-I1) + S)/I3,
    Scale is 1.0/Omega,
    Shift is -Phi/Omega,
    scale_and_shift(Xs, Scale, Shift, Scaled_And_Shifted),
    clamp(Scaled_And_Shifted, -1, 1, Ys).

/*  Library predicates  */
/*  All of these are or should be in your Prolog library,
    possibly with a different name and interface,
    except for scale_and_shift/4, clamp/4, abs_list/2.
    I'm a bit embarrassed about those, to tell you the
    truth, but NONE of these functions is coupled to its
    uses in the preceding code; ALL are reusable.
*/
unzip([], [], []).
unzip([(X,Y)|Pairs], [X|Xs], [Y|Ys]) :-
    unzip(Pairs, Xs, Ys).

dot(Xs, Ys, Dot) :-
    dot(Xs, Ys, 0, Dot).

%  NB: at first sight, dot/4 should be private to dot/3,
%  but I have *VERY* often had a use for calling it directly
%  instead of calling dot/3.

dot([], [], Dot, Dot).
dot([X|Xs], [Y|Ys], Dot0, Dot) :-
    Dot1 is Dot0 + X*Y,
    dot(Xs, Ys, Dot1, Dot).

sum(Xs, Sum) :-
    sum(Xs, 0, Sum).

%  NB: at first sight, sum/3 should be private to sum/2,
%  but again, I find it useful to call sum/3 directly about
%  as often as it's useful to call sum/2.

sum([], Sum, Sum).
sum([X|Xs], Sum0, Sum) :-
    Sum1 is Sum0 + X,
    sum(Xs, Sum1, Sum).

scale_and_shift([], _, _, []).
scale_and_shift([X|Xs], A, B, [Y|Ys]) :-
    Y is A*X+B,
    scale_and_sfhit(Xs, A, B, Ys).

clamp([], _, _, []).
clamp([X|Xs], L, U, [Y|Ys]) :-
    (   X < L -> Y = L
    ;   X > U -> Y = U
    ;            Y = X
    ),
    clamp(Xs, L, U, Ys).

count(Template, List, Condition, Count) :-
    findall(*, ( member(Template, List), Condition ), L),
    length(L, Count).

abs_list([], []).
abs_list([X|Xs], [Y|Ys]) :-
    Y is abs(X),
    abs_list(Xs, Ys).

drop(N, [_|Xs], Ys) :- N > 0, !,
    N1 is N - 1,
    drop(N1, Xs, Ys).
drop(_, Xs, Xs).

take(N, [X|Xs], [X|Ys]) :- N > 0, !,
    N1 is N - 1,
    take(N1, Xs, Ys).
take(0, _, []) :- !.
take(_, [], []).

nth0(N, List, X) :-
    (   integer(N) ->
    N >= 0,
    nth0i(N, List, X)
    ;   var(X) ->
    nth0v(L, X, 0, N)
    ;   abort
    ).

nth0v([X|_], X, N, N).
nth0v([_|Xs], X, N0, N) :-
    N1 is N0 + 1,
    nth0v(Xs, X, N1, N).

nth0i(N, {X|Xs], V) :-
    (   N =:= 0 -> V = X
    ;   N1 is N - 1,
        nth0i(N1, Xs, V)
    ).

Oh, let's clean that up a bit. The preferred way to take a section of a list in Prolog is not to use take and drop but to use length:sublist.

    drop(I1, Sorted_Xs, Mid_And_High_Xs),
    take(I3, Mid_And_High_Xs, Mid_Xs),

should be

    length:sublist(Sorted_Xs, Mid_Xs, I1, I3, I2)

We can do list comprehensions with findall/3:

    Median_Shift is -Median,
    scaled_and_shifted(Xs, 1, Median_Shift, Ms),
    abs_list(Ms, Ws),

could be

    findall(W, (member(X, Xs), W is abs(X-Median)), Ws)

and

    Scale is 1.0/Omega,
    Shift is -Phi/Omega,
    scale_and_shift(Xs, Scale, Shift, Scaled_And_Shifted),
    clamp(Scaled_And_Shifted, -1, 1, Ys).

could be

    findall(Y, ( member(X, Xs), Y is min(1, max(-1, (X-Phi)/Omega)) ), Ys)

I just wish findall/3 were as cheap as "map", but it isn't.

> but they wouldn't be used in Prolog, if they were enabled?

I dare say they WOULD be used in Prolog. *BUT* "enabling" nested definitions in Prolog would not be a simple matter of flipping a switch (as "enabled" suggests), but of fairly major design and implementation work. And the compiler would have to implement them the way a Haskell compiler does:  by turning them into UN-nested functions.

> I could try to guess that this
> may have something to do with the more pervasive higher-order
> style in Haskell, but I would think that people also use
> "where"s in first-order Haskell programs.

Of course they do.  They use 'where' a LOT in Haskell, for the simple reason that if you want to name an intermediate result (that is NOT a function) you have to use "let" or "where".

> I don't have metrics results that back up my guess.  I could
> come up with some other guesses; I have indeed some candidates,
> but perhaps someone *knows*.

Take a look at Mercury.  It combines logic programming and functional programming, and it is quite good at supporting higher order programming, and it HAS anonymous nested function and predicate definitions (tamed by the mode system so that higher-order unification is never needed).

In short, if you don't like Prolog, try Mercury.  It may be exactly what you want.  (And it requires a very complex implementation, but someone has already done that.)

> In fact, the (first-order) fib function appears to me as a reasonable
> example of a function that benefits from a local helper. Richard says
> "An auxiliary predicate having been defined, you often find uses for it
> elsewhere. (For example, my fib/3 can also be used to calculate Lucas
> numbers.)" I get the point for fib/3 but the general argument seems to
> lead to a slippery slope because it sounds a bit like let's presume most
> abstractions are reusable in perhaps unanticipated ways, so better
> expose them, so better export almost everything.

No, I did *NOT* say "export".  In fact I said the explicit opposite of that.  I said to ENCAPSULATE functions that would have been nested, but to encapsulate them using MODULES.

In fact the example I presented above is a good (because entirely honest) demonstration of my point:  with the single exception of scaled_and_clamped/3, every single auxiliary predicate I needed either *was* in my library already or could profitably be put there.  Ones that weren't already in the library would not be *EXPORTED*; they would be kept at top level until they were needed in another module at which point they would be moved out of this module entirely and into an appropriate module in the library.

> Below, I list some ways to transcribe O'Keefe's fib to Haskell. The
> first one is the more sensible one for the case of fib; it associates
> the helper with the relevant equation. The other two variations
> illustrate notational options -- one can make it so that a helper is
> accessible by several "cases". In reality, one uses local functions for
> more than just "hiding" or "grouping with a client", i.e., they are used
> as means to take advantage of the existing argument bindings for the
> parent. The fib example cannot make interesting use of this possibility
> but here is a lambda-dropped version of append that binds ys at the top:
>
> append xs ys = append xs
>  where
>   append [] = ys
>   append (x:xs) = x : append xs

And do you know what a Haskell compiler does with this? It turns it into

    append xs ys = (append' ys) xs

    append' ys [] = ys
    append' ys (x:xs) = x : (append' ys) xs

which is just the original definition twisted around a bit. One has to ask, what *is* the point?  Why move something in when it's going to be moved straight out again?

In fact there is a specific feature of Haskell which means that nested functions are often better as un-nested functions (with export being controlled in Haskell, as in SWI Prolog, by the module system). That's the well known scope-of-type-variables problem.

> -- Attach the helper to the relevant equation
> fib 0 = 1
> fib n = fib n 1 1
>  where fib 1 x _ = x
>        fib n x y = fib (n-1) (x+y) x

Which the Haskell compiler turns into

    fib 0 = 1
    fib n = fib' n 1 1

    fib' 1 x _ = x
    fib' n x y = fib' (n-1) (x+y) x

The nested function is HARDER to read because the human reader has to ask "WHY is this function nested?  HOW is it coupled to its surroundings?" In this case, the answer turns out to be "it isn't", which causes a justified feeling of resentment: "why, Mr Author, did you make me do all that decoding work for nothing?"

> -- Factor for a single binding, multiple RHSs
> fib n | n == 0    = 1
>       | otherwise = fib n 1 1
>  where fib 1 x _ = x
>        fib n x y = fib (n-1) (x+y) x

This has all the disadvantages of the previous version, plus some scoping weirdness:  'where' definitions are available in multiple alternatives for a single rule, but not for multiple rules.  Again, this is far more readable if the nested function is NOT nested.

Just to try, possibly vainly, to avoid any misconception: I am NOT saying that nested functions are undesirable or inappropriate or anything like that in Haskell.  What I *am* saying is that if an auxiliary function isn't *coupled* to its context through one or more shared variables, it is almost always better to un-nest it.  (I wrote the pbcor function at the top, and the only reason dot is declared inside is sheer laziness; I am sorry I did that.)



From: Bart Demoen
To: SWI-Prolog mailing list
Subject: Re: [SWIPL] Nested predicates?
Date: Tue, 28 Mar 2006 21:47:39 +0200

Richard A. O'Keefe wrote:
> if an auxiliary function isn't *coupled* to its context through one
> or more shared variables, it is almost always better to un-nest it.
> ...
> the only reason dot is declared inside is sheer laziness

[I didn't read all of Richard's mail]

I find the ability to nest functions/predicates attractive because I can often vouch for "this definition is correct when used as in the context just surrounding it" but not for any larger context. That's lazyness, or lack of time, but most of all it is trying to err on the safe side: don't make things public that you're not prepared to "maintain".

"*coupled* to its context through one or more shared variables" is one aspect of coupling to a context - it couls also be because of preconditions that are context dependent.



From: Richard A. O'Keefe
To: SWI-Prolog mailing list
Subject: Re: [SWIPL] Nested predicates?
Date: Wed, 29 Mar 2006 16:51:25 +1200 (NZST)

I wrote:
>> if an auxiliary function isn't *coupled* to its context through one
>> or more shared variables, it is almost always better to un-nest it.

Bart Demoen replied:
> I find the ability to nest functions/predicates attractive because I
> can often vouch for "this definition is correct when used as in the
> context just surrounding it" but not for any larger context.

This brings us back to the discussion about style that we were having a while back.  What is to the advantage of the *writer* of a piece of code is not always to the advantage of the *reader*.

To me the important question is "how will someone ELSE, reading this code, KNOW what the relevant context actually is?"

> That's lazyness, or lack of time, but most of all it is trying to err
> on the safe side: don't make things public that you're not prepared to
> "maintain".

This is a straw man.  Consider this table:
public not public
nested possible possible
not nested possible possible


I have been, and remain, concerned solely with the nested/not nested question.  This is entirely orthogonal to the public/not public question. All four combinations are theoretically possible and all four of them are supported in some programming language or other.  (Lisp, for instance.)

Making something "not nested" is very very VERY different from making it public.

> "*coupled* to its context through one or more shared variables" is one
> aspect of coupling to a context - it could also be because of
> preconditions that are context dependent.

Right.  Which is why simply putting one routine inside another one is NOT enough all by itself.  A future reader (including yourself a few months later) needs to know WHAT the relevant context is.  That means commenting it.

Now, take an example from my earlier message:

    nth0(N, List, X) :-
    (   integer(N) ->
        N >= 0,
        nth0i(N, List, X)
    ;   var(X) ->
        nth0v(L, X, 0, N)
    ;   abort
    ).

    nth0v([X|_], X, N, N).
    nth0v([_|Xs], X, N0, N) :-
    N1 is N0 + 1,
    nth0v(Xs, X, N1, N).

    nth0i(N, {X|Xs], V) :-
    (   N =:= 0 -> V = X
    ;   N1 is N - 1,
        nth0i(N1, Xs, V)
    ).

nth0/3 is exported from the module it is defined in. It is "public" and "not nested".

nth0v/4 and nth0i/3 are *not* exported from that module. They are "not public" and "not nested".

Making them "not nested" means a big benefit for a human reader: whatever the context might be (if any) that links them to nth0/3, it *isn't* shared variables.  When I am trying to understand a variable in nth0i/3, I do not have to look anywhere else at all.

There _is_ a coupling between these predicates and their caller, and in the real code, it's explained in the comments I stripped out to keep the previous message short:

    %.  nth0v(?List: list(T), ?Element: T, +N0: integer, -N: integer).
    %.  nth0i(?List: list(T), ?Element: T, +I: integer).

So, would this be better if we made these predicates nested?

    nth0(N, List, X) :-
    (   integer(N) ->
        N >= 0,
        nth0i(N, List, X)
        where {|
        nth0i(N, {X|Xs], V) :-
            (   N =:= 0 -> V = X
            ;   N1 is N - 1,
            nth0i(N1, Xs, V)
            )
        |}
    ;   var(X) ->
        nth0v(L, X, 0, N)
        where {|
        nth0v([X|_], X, N, N);;
        nth0v([_|Xs], X, N0, N) :-
            N1 is N0 + 1,
            nth0v(Xs, X, N1, N)
        |}
    ;   abort
    ).

I don't think so.  We run into a prlblem:  are N and X the same thing inside nth0i/3 as they are in nth0/3, and if not, why not?  The sheer bulk makes this version of nth0/3 hard to read.

But most tellingly, there is *also* in that library a predicate

    nth1(N, L, X) :-
        (   integer(N) ->
            N >= 1,
            M is N - 1,
            nth0i(M, L, X)
        ;   var(N) ->
            nth0v(L, X, 1, N)
        ;   abort
        ).

Yes, those predicates depended on instantiation state guaranteed by the caller, BUT another caller within the same module could make the same guarantees.

If routines are unnested ("lambda-lifted") and the context conditions are made EXPLICIT, then
Make no mistake. I have known and loved Algol-like languages for a long time. (The second non-trivial program I ever worked on was a port of Wirth's Euler compiler.) I have known and loved Lisp-like languages for a long time. I regard the lack of nesting functions in C++ as a serious flaw. Give me a language that allows nested definitions, and I will use them.

I'm not arguing that nested definitions are a bad idea.
I'm making two claims:
  1. *implicit* coupling is not good.
  2. One of the main advantages of Prolog is its simplicity; adding nested definitions to Prolog would make it a much more complicated  language, and empirically, the benefit would not be great.

As I've said before, if I want Mercury, I know where to find it. (~/export/mercury.d/, as it happens.)